package com.jack006.article.controller;

import com.jack006.api.BaseController;
import com.jack006.api.controller.article.ArticlePortalControllerApi;
import com.jack006.api.controller.user.UserControllerApi;
import com.jack006.article.service.ArticlePortalService;
import com.jack006.grace.result.GraceJSONResult;
import com.jack006.grace.result.ResponseStatusEnum;
import com.jack006.pojo.Article;
import com.jack006.pojo.eo.ArticleEO;
import com.jack006.pojo.vo.AppUserVO;
import com.jack006.pojo.vo.ArticleDetailVO;
import com.jack006.pojo.vo.IndexArticleVO;
import com.jack006.utils.IpUtils;
import com.jack006.utils.JsonUtils;
import com.jack006.utils.PagedGridResult;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @Classname ArticleController
 * @Description 文章业务实现
 * @Date 2021-06-10 08:15
 * @Created by jack
 */
@RestController
public class ArticlePortalController extends BaseController implements ArticlePortalControllerApi {

    final static Logger logger = LoggerFactory.getLogger(ArticlePortalController.class);


    @Autowired
    private ArticlePortalService articlePortalService;
    @Resource
    private UserControllerApi userControllerApi;


    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Override
    public GraceJSONResult eslist(String keyword, Integer category, Integer page, Integer pageSize) {

        /**
         * es 查询文章列表
         *  1、查询所有
         *  2、分类查询
         *  3、关键字查询
         */

        // es 的页面是从0开始计算的，所以在这里 page 需要-1
        if (page < 1) return null;
        page --;

        Pageable pageable = PageRequest.of(page,pageSize);

        SearchQuery query = null;
        AggregatedPage<ArticleEO> pagedArticle = null;

        // 第一种 查询所有
        if (StringUtils.isBlank(keyword) && category == null) {
            query = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.matchAllQuery())
                    .withPageable(pageable)
                    .build();
            pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
        }

        // 第二种 分类查询
        if (StringUtils.isBlank(keyword) && category != null) {
            query = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.termQuery("categoryId",category))
                    .withPageable(pageable)
                    .build();
            pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
        }

        // 第三种 关键字查询
//        if (StringUtils.isNotBlank(keyword) && category == null) {
//            query = new NativeSearchQueryBuilder()
//                    .withQuery(QueryBuilders.matchQuery("title",keyword))
//                    .withPageable(pageable)
//                    .build();
//        }

        // 第三种，关键字搜索高亮显示
        String searchTitleField = "title";
        if (StringUtils.isNotBlank(keyword) && category == null) {
            String preTag = "<font color='red'>";
            String postTag = "</font>";
            query = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.matchQuery("title",keyword))
                    .withPageable(pageable)
                    .withHighlightFields(new HighlightBuilder.Field(searchTitleField)
                            .preTags(preTag)
                            .postTags(postTag)
                     )
                    .build();

            pagedArticle = esTemplate.queryForPage(query, ArticleEO.class, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass,
                                                        Pageable pageable) {

                    List<ArticleEO> articleHighLightList = new ArrayList<>();
                    SearchHits hits = searchResponse.getHits();
                    for (SearchHit h : hits) {
                        HighlightField highlightField = h.getHighlightFields().get(searchTitleField);
                        String title = highlightField.getFragments()[0].toString();

                        // 获得其他字段信息
                        String articleId = (String)h.getSourceAsMap().get("id");
                        Integer categoryId = (Integer)h.getSourceAsMap().get("categoryId");
                        Integer articleType = (Integer)h.getSourceAsMap().get("articleType");
                        String articleCover = (String)h.getSourceAsMap().get("articleCover");
                        String publishUserId = (String)h.getSourceAsMap().get("publishUserId");
                        Long dateLong = (Long)h.getSourceAsMap().get("publishTime");
                        Date publishTime = new Date(dateLong);

                        ArticleEO articleEO = new ArticleEO();
                        articleEO.setId(articleId);
                        articleEO.setCategoryId(categoryId);
                        articleEO.setArticleType(articleType);

                        articleEO.setTitle(title);

                        articleEO.setArticleCover(articleCover);
                        articleEO.setPublishUserId(publishUserId);
                        articleEO.setPublishTime(publishTime);

                        articleHighLightList.add(articleEO);
                    }
                    return new AggregatedPageImpl<>((List<T>)articleHighLightList,
                            pageable,
                            searchResponse.getHits().totalHits);
                }

                @Override
                public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
                    return null;
                }
            });
        }

        // 重组文章列表
        // AggregatedPage<ArticleEO> pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
        List<ArticleEO> content = pagedArticle.getContent();
        List<Article> articleList = new ArrayList<>();
        for (ArticleEO articleEO : content) {
            System.out.println(articleEO);
            Article article = new Article();
            BeanUtils.copyProperties(articleEO,article);
            articleList.add(article);
        }

        // 重新封装之前的 grid格式
        PagedGridResult gridResult = new PagedGridResult();
        gridResult.setRows(articleList);
        gridResult.setPage(page +1);
        gridResult.setTotal(pagedArticle.getTotalPages());
        gridResult.setRecords(pagedArticle.getTotalElements());

        gridResult = rebuildArticleGrid(gridResult);

        return GraceJSONResult.ok(gridResult);
    }

    @Override
    public GraceJSONResult list(String keyword, Integer categoryId, Integer page, Integer pageSize) {

        if (page == null) {
            page = COMMON_START_PAGE;
        }
        if (pageSize == null) {
            pageSize = COMMON_PAGE_SIZE;
        }
        PagedGridResult gridResult = articlePortalService.queryArticleList(keyword,categoryId,page,pageSize);

        gridResult = rebuildArticleGrid(gridResult);
        return GraceJSONResult.ok(gridResult);
    }

    private PagedGridResult rebuildArticleGrid(PagedGridResult gridResult) {
        // 查询用户 Id
        List<Article> list = (List<Article>) gridResult.getRows();
        // 1、构建发布者 Id列表
        Set<String> idSet = new HashSet<>();
        List<String> articleIdList = new ArrayList<>();
        for (Article article : list) {
            // 1.1 构建发布者 ID List
            idSet.add(article.getPublishUserId());
            // 1.2 构建文章 ID List
            articleIdList.add(REDIS_ARTICLE_READ_COUNTS+":"+article.getId());

        }
        // 1.3 发起 redis 批量查询
        List<String> readCountsRedisList = redisOperator.mget(articleIdList);
        // 2、发起远程调用，请求用户服务获取用户信息
        List<AppUserVO> publisherList = getBasicUserList(idSet);
        // 3、重组文章列表
        List<IndexArticleVO> indexArticleList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            IndexArticleVO indexArticleVO = new IndexArticleVO();
            Article a = list.get(i);
            BeanUtils.copyProperties(a, indexArticleVO);

            // 3.1 从publisherList中获得发布者的基本信息
            AppUserVO publisher  = getUserIfPublisher(a.getPublishUserId(), publisherList);
            indexArticleVO.setPublisherVO(publisher);

            // 3.2 重新组装设置文章列表中的阅读量
            String redisCountsStr = readCountsRedisList.get(i);
            int readCounts = 0;
            if (StringUtils.isNotBlank(redisCountsStr)) {
                readCounts = Integer.valueOf(redisCountsStr);
            }
            indexArticleVO.setReadCounts(readCounts);

            indexArticleList.add(indexArticleVO);
        }
        gridResult.setRows(indexArticleList);
        return gridResult;
    }


    /**
     * 发起远程调用，获得用户的基本信息
     * @param idSet
     * @return
     */
    public List<AppUserVO> getBasicUserList(Set<String> idSet) {
//        String serviceId = "SERVICE-USER";
//        String userServerUrlExecute = "http://"+serviceId+"/user/queryByIds?userIds="+ JsonUtils.objectToJson(idSet);
//        ResponseEntity<GraceJSONResult> responseEntity = restTemplate.getForEntity(userServerUrlExecute, GraceJSONResult.class);
//        GraceJSONResult bodyResult = responseEntity.getBody();
        GraceJSONResult bodyResult = userControllerApi.queryByIds(JsonUtils.objectToJson(idSet));
        List<AppUserVO> publisherList = null;
        if (bodyResult.getStatus() == 200) {
            String userJson = JsonUtils.objectToJson(bodyResult.getData());
            publisherList = JsonUtils.jsonToList(userJson, AppUserVO.class);
        } else {
            publisherList = new ArrayList<>();
        }
        return publisherList;
    }

    private AppUserVO getUserIfPublisher(String publisherId,
                                         List<AppUserVO> publisherList) {
        for (AppUserVO user : publisherList) {
            if (user.getId().equalsIgnoreCase(publisherId)) {
                return user;
            }
        }
        return null;
    }

    @Override
    public GraceJSONResult hotList() {
        List<Article> articles = articlePortalService.queryHotList();
        return GraceJSONResult.ok(articles);
    }


    @Override
    public GraceJSONResult queryArticleListOfWriter(String writerId, Integer page, Integer pageSize) {

        if (StringUtils.isBlank(writerId)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.ARTICLE_QUERY_PARAMS_ERROR);
        }

        if (page == null) {
            page = COMMON_START_PAGE;
        }
        if (pageSize == null) {
            pageSize = COMMON_PAGE_SIZE;
        }

        PagedGridResult gridResult = articlePortalService.queryArticleListOfWriter(writerId,page,pageSize);
        gridResult = rebuildArticleGrid(gridResult);
        return GraceJSONResult.ok(gridResult);
    }

    @Override
    public GraceJSONResult queryGoodArticleListOfWriter(String writerId) {
        PagedGridResult gridResult = articlePortalService.queryGoodArticleListOfWriter(writerId);
        gridResult = rebuildArticleGrid(gridResult);
        return GraceJSONResult.ok(gridResult);
    }

    @Override
    public GraceJSONResult detail(String articleId) {

        if (StringUtils.isBlank(articleId)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.ARTICLE_ID_IS_NULL_ERROR);
        }
        ArticleDetailVO detailVO = articlePortalService.queryDetail(articleId);

        Set<String> idSet = new HashSet<>();
        idSet.add(detailVO.getPublishUserId());
        List<AppUserVO> publisherList = getBasicUserList(idSet);

        if (!publisherList.isEmpty()) {
            detailVO.setPublishUserName(publisherList.get(0).getNickname());
        }

        detailVO.setReadCounts(getCountsFormRedis(REDIS_ARTICLE_READ_COUNTS+":"+articleId));

        return GraceJSONResult.ok(detailVO);
    }

    @Override
    public Integer readCounts(String articleId) {
        Integer count = getCountsFormRedis(REDIS_ARTICLE_READ_COUNTS + ":" + articleId);
        return count;
    }

    @Override
    public GraceJSONResult readArticle(String articleId, HttpServletRequest request) {

        String userIp = IpUtils.getRequestIp(request);
        // 设置针对当前用户 Ip 的永久存在的 key,存入到 redis，表示改 ip的用户已经阅读过
        redisOperator.setnx(REDIS_ALREADY_READ+":"+articleId+":"+userIp,userIp);

        if (StringUtils.isBlank(articleId)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.ARTICLE_ID_IS_NULL_ERROR);
        }
        redisOperator.increment(REDIS_ARTICLE_READ_COUNTS+":"+articleId,1);

        return GraceJSONResult.ok();
    }
}
